#include "../Basic/String.h"
#include "RegexExpression.h"

namespace zl
{
	namespace regex_internal
	{
		bool       IsChar(const wchar_t* &input,wchar_t c)
		{
			if(*input==c)
			{
				input++;
				return true;
			}
			else
			{
				return false;
			}
		}

		bool        IsChars(const wchar_t* &input,wchar_t* chars,wchar_t &c)
		{
			const wchar_t* position=wcschr(chars,*input);
			if(position)
			{
				c=*input++;
				return true;
			}
			else
			{
				return false;
			}
		}

		bool         IsStr(const wchar_t* &input,wchar_t* str)
		{
			size_t len=wcslen(str);
			if(wcsncmp(input,str,len)==0)
			{
				input+=len;
				return true;
			}
			else
			{
				return false;
			}	
		}

		bool          IsChars(const wchar_t* &input,wchar_t* chars)
		{
			wchar_t c;
			return IsChars(input,chars,c);
		}

		bool          IsPositiveInteger(const wchar_t* &input,zint &number)
		{
            bool readed=false;
			number=0;
			while(L'0'<=*input&&*input<=L'9')
			{
				number=number*10+(*input++)-L'0';
				readed=true;
			}
			return readed;
		}

		bool           IsName(const wchar_t* &input,std::wstring& name)
		{
			const wchar_t* read=input;
			if((*read>=L'a'&&*read<=L'z')||(*read>=L'A'&&*read<=L'Z')||*read==L'_')
			{
				read++;
				while((*read>=L'a'&&*read<=L'z')||(*read>=L'A'&&*read<=L'Z')
					||(*read>=L'0'&&*read<=L'9')||*read==L'_')
				{
					read++;
				}
			}
			if(read==input)
			{
				return false;
			}
			else
			{
				name = std::wstring(input, (zint)(read - input));
				input=read;
				return true;
			}
		}

		
		Ptr<Expression>       ParseFunction(const wchar_t* &input)
		{   
			if(IsStr(input,(wchar_t*)L"(=")) 
			{
				Ptr<Expression> sub=ParseExpression(input);
				if(!IsChar(input,L')'))
				{
					goto NEED_RIGHT_BRACKET;
				}
				PositiveExpression* expression=new PositiveExpression;
				expression->expression=sub;
				return expression;
			}
			else if(IsStr(input,(wchar_t*)L"(!")) 
			{
				Ptr<Expression> sub=ParseExpression(input);
				if(!IsChar(input,L')'))
				{
					goto NEED_RIGHT_BRACKET;
				}
				NegativeExpression* expression=new NegativeExpression;
				expression->expression=sub;
				return expression;
			}
			else if(IsStr(input,(wchar_t*)L"(<&"))
			{
				std::wstring name;
				if(!IsName(input, name))
				{
					goto NEED_NAME;
				}
				if(!IsChar(input, L'>'))
				{
					goto NEED_GREATER;
				}
				if(!IsChar(input, L')'))
				{
					goto NEED_RIGHT_BRACKET;
				}
				UsingExpression* expression=new UsingExpression;
				expression->name=name;
				return expression;
			}
			else if(IsStr(input,(wchar_t*)L"(<$"))
			{
				std::wstring name;
				zint index=-1;
				if(IsName(input,name))
				{
					if(IsChar(input, L';'))
					{
						if(!IsPositiveInteger(input, index))
						{
							goto NEED_NUMBER;
						}
					}
				}
				else if(!IsPositiveInteger(input, index))
				{
					goto NEED_NUMBER;
				}
				if(!IsChar(input, L'>'))
				{
					goto NEED_GREATER;
				}
				if(!IsChar(input, L')'))
				{
					goto NEED_RIGHT_BRACKET;
				}
				MatchExpression* expression=new MatchExpression;
				expression->name=name;
				expression->index=index;
				return expression;
			}
			else if(IsStr(input,(wchar_t*)L"(<"))
			{
				std::wstring name;
				if(!IsName(input, name))
				{
					goto NEED_NAME;
				}
				if(!IsChar(input, L'>'))
				{
					goto NEED_GREATER;
				}
				Ptr<Expression> sub=ParseExpression(input);
				if(!IsChar(input, L')'))
				{
					goto NEED_RIGHT_BRACKET;
				}
				CaptureExpression* expression=new CaptureExpression;
				expression->name=name;
				expression->expression=sub;
				return expression;
			}
			else if(IsStr(input,(wchar_t*)L"(?"))
			{
				Ptr<Expression> sub=ParseExpression(input);
				if(!IsChar(input, L')'))
				{
					goto NEED_RIGHT_BRACKET;
				}
				CaptureExpression* expression=new CaptureExpression;
				expression->expression=sub;
				return expression;
			}
			else if(IsChar(input,L'('))
			{
				Ptr<Expression> sub=ParseExpression(input);
				if(!IsChar(input,L')'))
				{
					goto NEED_RIGHT_BRACKET;
				}
				return sub;
			}
			else
			{
				return 0;
			}
NEED_RIGHT_BRACKET:
			throw("Regular expression syntax error: \")\" expected.");
NEED_GREATER:
			throw("Regular expression syntax error: \">\" expected.");
NEED_NAME:
			throw("Regular expression syntax error: Identifier expected.");
NEED_NUMBER:
			throw("Regular expression syntax error: Number expected.");
		}

		
		Ptr<Expression>        ParseCharset(const wchar_t* &input)
		{
			if(!*input)
			{
				return 0;
			}
			else if(IsChar(input,L'^'))
			{
				return new BeginExpression;
			}
			else if(IsChar(input,L'$'))
			{
				return new EndExpression;
			}
			
			else if(IsChar(input,L'\\')||IsChar(input,L'/'))
			{
				Ptr<CharSetExpression> expression=new CharSetExpression();
				expression->reverse=false;
				switch(*input)
				{
				case L'.':
					expression->ranges.add(CharRange(1,65535));
					break;
				case L'r':
					expression->ranges.add(CharRange(L'\r',L'\r'));
					break;
				case L'n':
					expression->ranges.add(CharRange(L'\n',L'\n'));
					break;
				case L't':
					expression->ranges.add(CharRange(L'\t',L'\t'));
					break;
                case L'\\':case L'/':case L'(':case L')':case L'+':case L'*':case L'?':case L'|':
				case L'{':case L'}':case L'[':case L']':case L'<':case L'>':
				case L'^':case L'$':case L'!':case L'=':
					expression->ranges.add(CharRange(*input, *input));
					break;
				case L's':
					expression->ranges.add(CharRange(L' ',L' '));
					expression->ranges.add(CharRange(L'\r',L'\r'));
					expression->ranges.add(CharRange(L'\n',L'\n'));
					expression->ranges.add(CharRange(L'\t',L'\t'));
					break;
				case L'd':
					expression->ranges.add(CharRange(L'0',L'9'));
					break;
				case L'l':
					expression->ranges.add(CharRange(L'_',L'_'));
					expression->ranges.add(CharRange(L'A',L'Z'));
					expression->ranges.add(CharRange(L'a',L'z'));
					break;
				case L'w':
					expression->ranges.add(CharRange(L'_',L'_'));
					expression->ranges.add(CharRange(L'0',L'9'));
					expression->ranges.add(CharRange(L'A',L'Z'));
					expression->ranges.add(CharRange(L'a',L'z'));
					break;
				default:
					throw("Regular expression syntax error: Illegal character set definition.");
					break;
				};
				input++;
				return expression;
			}
			else if(IsChar(input,L'['))
			{
				Ptr<CharSetExpression> expression=new CharSetExpression;
				if(IsChar(input,L'^'))
				{
					expression->reverse=true;
				}
				else
				{
					expression->reverse=false;
				}
				wchar_t a=L'\0';
				wchar_t b=L'\0';
				bool midState=false;
				while(true)
				{
					if(IsChar(input,L'\\')||IsChar(input,L'/'))
					{
						wchar_t c=L'\0';
						switch (*input)
						{
						case L'r':
							c=L'\r';
							break;
						case L'n':
							c=L'\n';
							break;
						case L't':
							c=L'\t';
							break;
						case L'-':case L'[':case L']':case L'\\':case L'/':case L'^':case L'$':
							c=*input;
							break;
						default:
							break;
						}
						input++;
						midState?b=c:a=c;
						midState=!midState;
					}
					else if(IsChars(input,(wchar_t*)L"-]"))
					{
						goto THROW_EXCEPTION;
					}
					else if(*input)
					{
						midState?b=*input++:a=*input++;
						midState=!midState;
					}
					else
					{
						goto THROW_EXCEPTION;
					}
					if(IsChar(input,L']'))
					{
						if(midState)
						{
							b=a;
						}
						if(!expression->AddRangewithConflict(CharRange(a,b)))
						{
							goto THROW_EXCEPTION;
						}
						break;
					}
					else if(IsChar(input,L'-'))
					{
						if(!midState)
						{
							goto THROW_EXCEPTION;
						}
					}
					else
					{
						if(midState)
						{
							b=a;
						}
						if(expression->AddRangewithConflict(CharRange(a,b)))
						{
							midState=false;
						}
						else
						{
							goto THROW_EXCEPTION;
						}
					}
				}
				return expression;
THROW_EXCEPTION:
				throw("Regular expression syntax error: Illegal character set definition.");
			}
			else if(IsChars(input,(wchar_t*)L"()+*?{}|"))
			{
				input--;
				return 0;
			}
			
			else
			{
				Ptr<CharSetExpression> expression=new CharSetExpression();
				expression->reverse=false;
				expression->ranges.add(CharRange(*input,*input));
				input++;
				return expression;
			}
		}

		
		Ptr<LoopExpression>    ParseLoop(const wchar_t* &input)
		{
			zint min=0;
			zint max=0;
			if(!*input)
			{
				return 0;
			}
			else if(IsChar(input,L'+'))
			{
				min=1;
				max=-1;
			}
			else if(IsChar(input,L'?'))
			{
				min=0;
				max=1;
			}
			else if(IsChar(input,L'*'))
			{
				min=0;
				max=-1;
			}
			else if(IsChar(input,L'{'))
			{
				if(IsPositiveInteger(input,min))
				{
					if(IsChar(input,L','))
					{
						if(!IsPositiveInteger(input,max))
						{
							max=-1;
						}
					}
					else
					{
						max=min;
					}
					if(!IsChar(input,L'}'))
					{
			            throw("Regular expression syntax error: Illegal loop expression.");
					}
				}
				else
				{
			      throw("Regular expression syntax error: Illegal loop expression.");
				}
			}
			else
			{
				return 0;
			}
			LoopExpression* expression=new LoopExpression();
			expression->min=min;
			expression->max=max;
			expression->preferlong=!IsChar(input, L'?');
			return expression;
		}

		
		Ptr<Expression>        ParseUint(const wchar_t* &input)
		{
			Ptr<Expression> uint=ParseCharset(input);
			if(!uint)
			{
				uint=ParseFunction(input);
			}
			if(!uint)
			{
				return 0;
			}
			Ptr<LoopExpression> loop;
			while(loop=ParseLoop(input))
			{
				loop->expression=uint;
				uint=loop;
			}
			return uint;
		}

		
		Ptr<Expression>        ParseJoin(const wchar_t* &input)
		{
			Ptr<Expression> expression=ParseUint(input);
			while(true)
			{
				Ptr<Expression> right=ParseUint(input);
				if(right)
				{
					SequenceExpression* sequence=new SequenceExpression;
					sequence->left=expression;
					sequence->right=right;
					expression=sequence;
				}
				else
				{
					break;
				}
			}
			return expression;
		}

		
		Ptr<Expression>        ParseAlt(const wchar_t* &input)
		{
			Ptr<Expression> expression=ParseJoin(input);
			while(true)
			{
				if(IsChar(input,L'|'))
				{
					Ptr<Expression> right=ParseJoin(input);
					if(right)
					{
						AlternateExpression* alternate=new AlternateExpression;
						alternate->left=expression;
						alternate->right=right;
						expression=alternate;
					}
					else
					{
						throw("Regular expression syntax error: Expression expected.");
					}
				}
				else
				{
					break;
				}
			}
			return expression;
		}

		Ptr<Expression>        ParseExpression(const wchar_t* &input)
		{
			return ParseAlt(input);
		}

		RegexExpression::Ref   ParseRegexExpression(const std::wstring& code)
		{
			RegexExpression::Ref regex=new RegexExpression;
			const wchar_t* start=code.c_str();
			const wchar_t* input=start;
				while(IsStr(input,(wchar_t*)L"(<#"))
				{
					std::wstring name;
					if(!IsName(input, name))
					{
						throw("Regular expression syntax error: Identifier expected.");
					}
					if(!IsChar(input, L'>'))
					{
					    throw(L"Regular expression syntax error: \">\" expected.");
					}
					Ptr<Expression> sub=ParseExpression(input);
					if(!IsChar(input, L')'))
					{
						throw("Regular expression syntax error: \")\" expected.");
					}
					if(regex->definitions.keys().contains(name))
					{
						throw ("Regular expression syntax error: Found duplicated sub expression name");
					}
					else
					{
						regex->definitions.add(name, sub);
					}
				}
				regex->expressions=ParseExpression(input);
				if(!regex)
				{
				   throw("Regular expression syntax error: Expression expected.");
				}
				if(*input)
				{
			       throw(L"Regular expression syntax error: Found unnecessary tokens.");
				}
				return regex;
		}

		std::wstring EscapeTextForRegex(const std::wstring& literalString)
		{
			std::wstring result;
			for(zint i=0;i<(zint)literalString.length();i++)
			{
				wchar_t c=literalString[i];
				switch(c)
				{
				case L'\\':case L'/':case L'(':case L')':case L'+':case L'*':case L'?':case L'|':
				case L'{':case L'}':case L'[':case L']':case L'<':case L'>':
				case L'^':case L'$':case L'!':case L'=':
					result += std::wstring(L"\\") + c;
					break;
				case L'\r':
					result+=L"\\r";
					break;
				case L'\n':
					result+=L"\\n";
					break;
				case L'\t':
					result+=L"\\t";
					break;
				default:
					result+=c;
				}
			}
			return result;
		}

		std::wstring UnescapeTextForRegex(const std::wstring& escapedText)
		{
			std::wstring result;
			for (zint i = 0; i<(zint)escapedText.length(); i++)
			{
				wchar_t c=escapedText[i];
				if(c==L'\\'||c==L'/')
				{
					if (i<(zint)escapedText.length() - 1)
					{
						i++;
						c=escapedText[i];
						switch(c)
						{
						case L'r':
							result+=L"\r";
							break;
						case L'n':
							result+=L"\n";
							break;
						case L't':
							result+=L"\t";
							break;
						default:
							result+=c;
						}
						continue;
					}
				}
				result+=c;
			}
			return result;
		}

		std::wstring NormalizeEscapedTextForRegex(const std::wstring& escapedText)
		{
			std::wstring result;
			for (zint i = 0; i<(zint)escapedText.length(); i++)
			{
				wchar_t c=escapedText[i];
				if(c==L'\\'||c==L'/')
				{
					if (i<(zint)escapedText.length() - 1)
					{
						i++;
						c=escapedText[i];
						result += std::wstring(L"\\") + c;
						continue;
					}
				}
				result+=c;
			}
			return result;
		}

		bool IsRegexEscapedListeralString(const std::wstring& regex)
		{
			for (zint i = 0; i<(zint)regex.length(); i++)
			{
				wchar_t c=regex[i];
				if(c==L'\\'||c==L'/')
				{
					i++;
				}
				else
				{
					switch(c)
					{
					case L'\\':case L'/':case L'(':case L')':case L'+':case L'*':case L'?':case L'|':
					case L'{':case L'}':case L'[':case L']':case L'<':case L'>':
					case L'^':case L'$':case L'!':case L'=':
						return false;
					}
				}
			}
			return true;
		}

	};
};
